package com.fand.persistent.support;

import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.simple.ParameterizedBeanPropertyRowMapper;

import com.fand.dto.PaginationDto;
import com.fand.persistent.builder.ModelBuilder;
import com.fand.template.support.TemplateSupport;
import com.fand.utils.PaginationUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
 * jdbc dao
 * 
 * @author yaoHT
 */
public class JdbcDaoSupport implements DaoSupport {
	/**
	 * namedParameterJdbcDaoSupport
	 */
	protected RouterNamedParameterJdbcTemplateSupport routerNamedParameterJdbcTemplateSupport;
	
	/**
	 * 模板支持
	 */
	protected TemplateSupport templateSupport;
	
	@Override
	public String builderSql(final String sqlId, final Map<String, Object> model) {
		final String sql = this.templateSupport.processTemplateIntoString(sqlId, model);
		
		return sql;
	}
	
	@Override
	public PaginationDto<Map<String, Object>> paginationForMapList(final String selectForObjectSqlId,
	        final String selectForMapListSqlId, final int paginationNo, final int paginationSize,
	        Map<String, Object> model) {
		final int totalSize = this.selectForObject(Integer.class, selectForObjectSqlId, null, model);
		
		final PaginationDto<Map<String, Object>> pagination = PaginationUtils.newPagination(paginationNo,
		        paginationSize, totalSize);
		
		List<Map<String, Object>> dataList;
		
		if (totalSize < 1) {
			dataList = Lists.newArrayList();
		} else {
			if (totalSize < pagination.getBeginRowNum()) {
				dataList = Lists.newArrayList();
			} else {
				if (model == null) {
					model = Maps.newHashMap();
				}
				
				model.put("BEGIN__ROW__NUM", pagination.getBeginRowNum());
				
				model.put("END__ROW__NUM", pagination.getEndRowNum());
				
				dataList = this.selectForMapList(selectForMapListSqlId, model);
			}
		}
		
		pagination.setDataList(dataList);
		
		return pagination;
	}
	
	@Override
	public <T> PaginationDto<T> paginationForMappedObjectList(final Class<T> mappedClass,
	        final String selectForObjectSqlId, final String selectForMappedObjectListSqlId,
	        final int paginationNo, final int paginationSize, Map<String, Object> model) {
		final int totalSize = this.selectForObject(Integer.class, selectForObjectSqlId, null, model);
		
		final PaginationDto<T> pagination = PaginationUtils
		        .newPagination(paginationNo, paginationSize, totalSize);
		
		List<T> dataList;
		
		if (totalSize < 1) {
			dataList = Lists.newArrayList();
		} else {
			if (totalSize < pagination.getBeginRowNum()) {
				dataList = Lists.newArrayList();
			} else {
				if (model == null) {
					model = Maps.newHashMap();
				}
				
				model.put("BEGIN__ROW__NUM", pagination.getBeginRowNum());
				
				model.put("END__ROW__NUM", pagination.getEndRowNum());
				
				dataList = this.selectForMappedObjectList(mappedClass, selectForMappedObjectListSqlId, model);
			}
		}
		
		pagination.setDataList(dataList);
		
		return pagination;
	}
	
	@Override
	public Map<String, Object> selectForMap(final String sqlId, final Map<String, Object> model) {
		final String sql = this.builderSql(sqlId, model);
		
		return this.routerNamedParameterJdbcTemplateSupport.getNamedParameterJdbcTemplate(sqlId, model)
		        .queryForMap(sql, model);
	}
	
	@Override
	public List<Map<String, Object>> selectForMapList(final String sqlId, final Map<String, Object> model) {
		final String sql = this.builderSql(sqlId, model);
		
		return this.routerNamedParameterJdbcTemplateSupport.getNamedParameterJdbcTemplate(sqlId, model)
		        .queryForList(sql, model);
	}
	
	@Override
	public <T> T selectForMappedObject(final Class<T> mappedClass, final String sqlId,
	        final Map<String, Object> model) {
		final String sql = this.builderSql(sqlId, model);
		
		try {
			return this.routerNamedParameterJdbcTemplateSupport.getNamedParameterJdbcTemplate(sqlId, model)
			        .queryForObject(sql, model, ParameterizedBeanPropertyRowMapper.newInstance(mappedClass));
		} catch (final EmptyResultDataAccessException e) {
			return null;
		}
	}
	
	@Override
	public <T> List<T> selectForMappedObjectList(final Class<T> mappedClass, final String sqlId,
	        final Map<String, Object> model) {
		final String sql = this.builderSql(sqlId, model);
		
		return this.routerNamedParameterJdbcTemplateSupport.getNamedParameterJdbcTemplate(sqlId, model).query(
		        sql, model, ParameterizedBeanPropertyRowMapper.newInstance(mappedClass));
	}
	
	@Override
	public <T> T selectForObject(final Class<T> requiredType, final String sqlId, final String objectId,
	        Map<String, Object> model) {
		if (StringUtils.isNotBlank(objectId)) {
			if (model == null) {
				model = Maps.newHashMap();
			}
			
			model.put(ModelBuilder.OBJECT_ID, objectId);
		}
		
		final String sql = this.builderSql(sqlId, model);
		
		return this.routerNamedParameterJdbcTemplateSupport.getNamedParameterJdbcTemplate(sqlId, model)
		        .queryForObject(sql, model, requiredType);
	}
	
	@Override
	public <T> List<T> selectForObjectList(final Class<T> elementType, final String sqlId, final String objectId,
	        Map<String, Object> model) {
		if (StringUtils.isNotBlank(objectId)) {
			if (model == null) {
				model = Maps.newHashMap();
			}
			
			model.put(ModelBuilder.OBJECT_ID, objectId);
		}
		
		final String sql = this.builderSql(sqlId, model);
		
		return this.routerNamedParameterJdbcTemplateSupport.getNamedParameterJdbcTemplate(sqlId, model)
		        .queryForList(sql, model, elementType);
	}
	
	public void setRouterNamedParameterJdbcTemplateSupport(
	        final RouterNamedParameterJdbcTemplateSupport routerNamedParameterJdbcTemplateSupport) {
		this.routerNamedParameterJdbcTemplateSupport = routerNamedParameterJdbcTemplateSupport;
	}
	
	public void setTemplateSupport(final TemplateSupport templateSupport) {
		this.templateSupport = templateSupport;
	}
	
	@Override
	public int update(final String sqlId, final List<Map<String, Object>> modelList) {
		if (CollectionUtils.isEmpty(modelList)) { return 0; }
		
		int rowCount = 0;
		
		for (final Map<String, Object> model : modelList) {
			rowCount += this.update(sqlId, model);
		}
		
		return rowCount;
	}
	
	@Override
	public int update(final String sqlId, final Map<String, Object> model) {
		final String sql = this.builderSql(sqlId, model);
		
		return this.routerNamedParameterJdbcTemplateSupport.getNamedParameterJdbcTemplate(sqlId, model).update(
		        sql, model);
	}
}
